package tk.mybatis.springboot.service.impl;

import org.assertj.core.util.DateUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.util.StringUtil;
import tk.mybatis.springboot.mapper.CourseMapper;
import tk.mybatis.springboot.mapper.UserMapper;
import tk.mybatis.springboot.model.*;
import tk.mybatis.springboot.service.CourseService;
import tk.mybatis.springboot.service.UserService;
import tk.mybatis.springboot.util.WxUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CourseServiceImpl implements CourseService {

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private UserMapper userMapper;

    private static final int K = 5; // 假设分为5个小班
    private static final int MAX_ITERATIONS = 100;

    @Override
    public List<Course> getMyCourse(Students student) {
        return courseMapper.getMyCourse(student.getId());
    }

    @Override
    public List<CourseSession> getCourseSession(String courseName) {
        String courseId = courseMapper.getCourseIdByName(courseName);
        return courseMapper.getCourseSessionById(courseId);
    }


    public String getClassNameById(String classId) {
        if (classId.equals("1")) {
            return "格物班";
        }
        if (classId.equals("2")) {
            return "致知班";
        }
        if (classId.equals("3")) {
            return "正心班";
        }
        if (classId.equals("4")) {
            return "诚意班";
        }
        return "格物班";
    }

    @Transactional(rollbackFor = Exception.class)
    public String signUpOld(StudentCourse studentCourse, Boolean upClass) {
        if (upClass) {
            if (userMapper.getCertificateNum(studentCourse.getStudentId(), String.valueOf(Integer.valueOf(studentCourse.getClassId()) - 1)) == 0) {
                Certificate certificate = new Certificate();
                certificate.setId(UUID.randomUUID().toString());
                certificate.setClassId(String.valueOf(Integer.valueOf(studentCourse.getClassId()) - 1));
                certificate.setClassName(getClassNameById(String.valueOf(Integer.valueOf(studentCourse.getClassId()) - 1)));
                certificate.setStudentId(studentCourse.getStudentId());
                certificate.setName(userMapper.getById(studentCourse.getStudentId()).getName());
                userMapper.insertCertificate(certificate);
            }
        }
        Integer num = courseMapper.getSignUpLog(studentCourse.getStudentId(), studentCourse.getClassId());
        if (num > 0) {
            return "您已经报名了";
        }
        //自动分班
        HashMap<String, String> classMap = userMapper.getCourseClass(studentCourse.getClassId());
        HashMap<String, String> largeGroupMap = userMapper.getLargeGroup(classMap.get("id"));
        HashMap<String, String> smallGroupMap = userMapper.getSmallGroup(largeGroupMap.get("id"));
        studentCourse.setStudentId(studentCourse.getStudentId());
        studentCourse.setCourseId(studentCourse.getClassId());
        studentCourse.setClassId(classMap.get("id"));
        studentCourse.setClassName(classMap.get("name"));
        studentCourse.setLargeGroupId(largeGroupMap.get("id"));
        studentCourse.setLargeGroupName(largeGroupMap.get("name"));
        studentCourse.setSmallGroupId(smallGroupMap.get("id"));
        studentCourse.setSmallGroupName(smallGroupMap.get("name"));
        studentCourse.setCurrentSessionNumber(studentCourse.getCurrentSessionNumber());
        courseMapper.signUp(studentCourse);
        return "报名成功";
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String signUp(StudentCourse studentCourse, Boolean upClass) {
        if (upClass) {
            if (userMapper.getCertificateNum(studentCourse.getStudentId(), String.valueOf(Integer.valueOf(studentCourse.getClassId()) - 1)) == 0) {
                Certificate certificate = new Certificate();
                certificate.setId(UUID.randomUUID().toString());
                certificate.setClassId(String.valueOf(Integer.valueOf(studentCourse.getClassId()) - 1));
                certificate.setClassName(getClassNameById(String.valueOf(Integer.valueOf(studentCourse.getClassId()) - 1)));
                certificate.setStudentId(studentCourse.getStudentId());
                certificate.setName(userMapper.getById(studentCourse.getStudentId()).getName());
                userMapper.insertCertificate(certificate);
            }
        }

        Integer num = courseMapper.getSignUpLog(studentCourse.getStudentId(), studentCourse.getClassId());
        if (num > 0) {
            return "您已经报名了";
        }

        // 获取当前班级已报名学生
        List<StudentCourse> existingStudents = userMapper.getStudentsByClassId(studentCourse.getClassId());
        // 获取当前学生信息
        Students currentStudent = userMapper.getById(studentCourse.getStudentId());
        // 将当前学生添加到待分班列表
        existingStudents.add(studentCourse);
        // 为当前学生分配最合适的小组
        try {
            // 使用K-means算法进行智能分班
            Map<String, List<StudentCourse>> groupedStudents = kmeansClustering(existingStudents);
            String smallGroupId = findBestGroupForStudent(currentStudent, groupedStudents,studentCourse);
            if (StringUtil.isEmpty(smallGroupId)) {
                System.out.println("由于样本过少，使用默认分班分组，不采用k-m算法");
                this.signUpOld(studentCourse, upClass);
                return "报名成功";
            }
            HashMap<String, String> smallGroupMap = userMapper.getSmallGroupById(smallGroupId);
            HashMap<String, String> largeGroupMap = userMapper.getLargeGroupById(smallGroupMap.get("large_group_id"));
            HashMap<String, String> classMap = userMapper.getCourseClass(largeGroupMap.get("class_id"));

            // 设置分班信息
            studentCourse.setCourseId(studentCourse.getClassId());
            studentCourse.setClassId(classMap.get("id"));
            studentCourse.setClassName(classMap.get("name"));
            studentCourse.setLargeGroupId(largeGroupMap.get("id"));
            studentCourse.setLargeGroupName(largeGroupMap.get("name"));
            studentCourse.setSmallGroupId(smallGroupMap.get("id"));
            studentCourse.setSmallGroupName(smallGroupMap.get("name"));

            // 保存报名信息
            courseMapper.signUp(studentCourse);
            return "报名成功";
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("由于km算法报错，采用默认分班分组");
            this.signUpOld(studentCourse, upClass);
            return "报名成功";
        }


    }

    // K-means聚类实现
    private Map<String, List<StudentCourse>> kmeansClustering(List<StudentCourse> students) {
        // 1. 特征提取：地区、年龄、职业
        List<StudentFeature> features = extractFeatures(students);

        // 2. 初始化K个中心点
        List<Centroid> centroids = initializeCentroids(features);

        // 3. 迭代直到收敛
        for (int i = 0; i < MAX_ITERATIONS; i++) {
            // 3.1 分配每个点到最近的中心点
            assignPointsToCentroids(features, centroids);

            // 3.2 更新中心点位置
            List<Centroid> newCentroids = updateCentroids(features, centroids.size());

            // 3.3 检查收敛
            if (isConverged(centroids, newCentroids)) {
                break;
            }

            centroids = newCentroids;
        }

        // 4. 构建分组结果
        Map<String, List<StudentCourse>> result = new HashMap<>();
        for (int i = 0; i < centroids.size(); i++) {
            final int groupIndex = i;
            List<StudentCourse> groupStudents = features.stream()
                    .filter(f -> f.assignedCentroidIndex == groupIndex)
                    .map(f -> f.studentCourse)
                    .collect(Collectors.toList());
            result.put("group_" + i, groupStudents);
        }

        return result;
    }

    // 特征提取类
    private static class StudentFeature {
        StudentCourse studentCourse;
        double[] features; // [地区特征, 年龄特征, 职业特征]
        int assignedCentroidIndex;

        public StudentFeature(StudentCourse studentCourse, double[] features) {
            this.studentCourse = studentCourse;
            this.features = features;
        }
    }

    // 中心点类
    private static class Centroid {
        double[] position;

        public Centroid(double[] position) {
            this.position = position;
        }
    }

    // 提取学生特征
    private List<StudentFeature> extractFeatures(List<StudentCourse> students) {
        List<StudentFeature> features = new ArrayList<>();

        // 获取所有地区和职业的集合，用于特征编码
        Set<String> allAddresses = new HashSet<>();
        Set<String> allOccupations = new HashSet<>();

        for (StudentCourse sc : students) {
            Students student = userMapper.getById(sc.getStudentId());
            if (student != null && student.getAddress() != null && student.getOccupation() != null) {
                allAddresses.add(student.getAddress());
                allOccupations.add(student.getOccupation());
            }
        }

        // 构建地区和职业的映射表
        Map<String, Integer> addressMap = new HashMap<>();
        int addressIndex = 0;
        for (String address : allAddresses) {
            addressMap.put(address, addressIndex++);
        }

        Map<String, Integer> occupationMap = new HashMap<>();
        int occupationIndex = 0;
        for (String occupation : allOccupations) {
            occupationMap.put(occupation, occupationIndex++);
        }

        // 提取每个学生的特征向量
        for (StudentCourse sc : students) {
            Students student = userMapper.getById(sc.getStudentId());
            if (student != null && student.getAddress() != null && student.getOccupation() != null) {
                // 特征向量：[地区特征, 年龄特征, 职业特征]
                double[] featureVector = new double[3];

                // 地区特征：使用简单的哈希编码
                featureVector[0] = addressMap.get(student.getAddress());

                // 年龄特征：直接使用年龄数值
                try {
                    featureVector[1] = Double.parseDouble(student.getAge());
                } catch (NumberFormatException e) {
                    featureVector[1] = 0; // 默认值
                }

                // 职业特征：使用简单的哈希编码
                featureVector[2] = occupationMap.get(student.getOccupation());

                features.add(new StudentFeature(sc, featureVector));
            }
        }

        return features;
    }

    // 初始化中心点
    private List<Centroid> initializeCentroids(List<StudentFeature> features) {
        List<Centroid> centroids = new ArrayList<>();
        Random random = new Random();

        // 随机选择K个点作为初始中心点
        for (int i = 0; i < K && i < features.size(); i++) {
            double[] position = Arrays.copyOf(features.get(random.nextInt(features.size())).features, 3);
            centroids.add(new Centroid(position));
        }

        return centroids;
    }

    // 分配点到最近的中心点
    private void assignPointsToCentroids(List<StudentFeature> features, List<Centroid> centroids) {
        for (StudentFeature feature : features) {
            double minDistance = Double.MAX_VALUE;
            int closestCentroidIndex = 0;

            for (int i = 0; i < centroids.size(); i++) {
                double distance = calculateDistance(feature.features, centroids.get(i).position);
                if (distance < minDistance) {
                    minDistance = distance;
                    closestCentroidIndex = i;
                }
            }

            feature.assignedCentroidIndex = closestCentroidIndex;
        }
    }

    // 计算欧氏距离
    private double calculateDistance(double[] point1, double[] point2) {
        double sum = 0;
        for (int i = 0; i < point1.length; i++) {
            sum += Math.pow(point1[i] - point2[i], 2);
        }
        return Math.sqrt(sum);
    }

    // 更新中心点位置
    private List<Centroid> updateCentroids(List<StudentFeature> features, int k) {
        List<Centroid> newCentroids = new ArrayList<>();

        for (int i = 0; i < k; i++) {
            final int centroidIndex = i;

            // 获取属于当前中心点的所有点
            List<StudentFeature> pointsInCluster = features.stream()
                    .filter(f -> f.assignedCentroidIndex == centroidIndex)
                    .collect(Collectors.toList());

            if (pointsInCluster.isEmpty()) {
                // 如果簇为空，重新随机初始化一个中心点
                Random random = new Random();
                double[] position = Arrays.copyOf(features.get(random.nextInt(features.size())).features, 3);
                newCentroids.add(new Centroid(position));
                continue;
            }

            // 计算新的中心点位置
            double[] newPosition = new double[3];
            for (StudentFeature feature : pointsInCluster) {
                for (int j = 0; j < 3; j++) {
                    newPosition[j] += feature.features[j];
                }
            }

            // 取平均值
            for (int j = 0; j < 3; j++) {
                newPosition[j] /= pointsInCluster.size();
            }

            newCentroids.add(new Centroid(newPosition));
        }

        return newCentroids;
    }

    // 检查是否收敛
    private boolean isConverged(List<Centroid> oldCentroids, List<Centroid> newCentroids) {
        if (oldCentroids.size() != newCentroids.size()) {
            return false;
        }

        for (int i = 0; i < oldCentroids.size(); i++) {
            double[] oldPos = oldCentroids.get(i).position;
            double[] newPos = newCentroids.get(i).position;

            for (int j = 0; j < oldPos.length; j++) {
                if (Math.abs(oldPos[j] - newPos[j]) > 0.001) {
                    return false;
                }
            }
        }

        return true;
    }

    // 为当前学生找到最合适的小组
    private String findBestGroupForStudent(Students student, Map<String, List<StudentCourse>> groups,StudentCourse studentCourse) {
        // 提取当前学生特征
        double[] studentFeatures = new double[3];

        // 地区特征
        studentFeatures[0] = student.getAddress() != null ? student.getAddress().hashCode() : 0;

        // 年龄特征
        try {
            studentFeatures[1] = Double.parseDouble(student.getAge());
        } catch (NumberFormatException e) {
            studentFeatures[1] = 0;
        }

        // 职业特征
        studentFeatures[2] = student.getOccupation() != null ? student.getOccupation().hashCode() : 0;

        // 计算学生与每个小组的中心点的距离，选择距离最远的小组（确保职业和年龄分散）
        String bestGroupKey = null;
        double maxDistance = -1;

        for (Map.Entry<String, List<StudentCourse>> entry : groups.entrySet()) {
            List<StudentCourse> groupStudents = entry.getValue();

            // 计算小组的中心点
            double[] groupCenter = new double[3];
            for (StudentCourse sc : groupStudents) {
                Students groupStudent = userMapper.getById(sc.getStudentId());
                if (groupStudent != null) {
                    // 地区特征
                    groupCenter[0] += groupStudent.getAddress() != null ? groupStudent.getAddress().hashCode() : 0;

                    // 年龄特征
                    try {
                        groupCenter[1] += Double.parseDouble(groupStudent.getAge());
                    } catch (NumberFormatException e) {
                        groupCenter[1] += 0;
                    }

                    // 职业特征
                    groupCenter[2] += groupStudent.getOccupation() != null ? groupStudent.getOccupation().hashCode() : 0;
                }
            }

            // 取平均值
            for (int i = 0; i < 3; i++) {
                groupCenter[i] /= groupStudents.size();
            }

            // 计算距离
            double distance = calculateDistance(studentFeatures, groupCenter);

            if (distance > maxDistance) {
                maxDistance = distance;
                bestGroupKey = entry.getKey();
            }
        }

        // 从bestGroupKey中提取簇索引（例如从"group_0"中提取0）
        if (bestGroupKey != null) {
            String[] parts = bestGroupKey.split("_");
            if (parts.length == 2) {
                try {
                    int clusterIndex = Integer.parseInt(parts[1]);

                    // 获取所有可用的小组
                    List<HashMap<String, String>> allSmallGroups = userMapper.getAllSmallGroups(studentCourse.getClassId());

                    // 根据簇索引分配到对应的小组
                    if (!allSmallGroups.isEmpty()) {
                        int groupIndex = clusterIndex % allSmallGroups.size();
                        return allSmallGroups.get(groupIndex).get("id");
                    }
                } catch (NumberFormatException e) {
                    System.out.println("无法解析簇索引: " + bestGroupKey);
                }
            }
        }

        // 如果出现异常，返回默认小组ID或抛出异常
        throw new RuntimeException("无法为学生分配合适的小组");
    }

}
